#!/usr/bin/python
# -*- coding: utf-8 -*-

#This file is part of pyLot library.
#
# pyLot is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyLot is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with pyLot.  If not, see <http://www.gnu.org/licenses/>.

__author__ = u'Pierre Puiseux, Guillaume Baty'
__copyright__ = u"Copyright 2011-2012 (C) andheo, Université de Pau et des Pays de l'Adour"
__credits__ = [u'Pierre Puiseux', u'Guillaume Baty']
__license__ = "GNU Lesser General Public License"

__date__ = u"16 mars 2010"
__author__ = u"INRIA pyLot"
__all__ = [u'ConfigOption', u'ConfigOptionContainer', u'Configuration', u'Settings']

from _pylot.minimal.datum import Datum
from _pylot.minimal.utils import check_type, check_value
from _pylot.minimal.system import ENCODING
from _pylot.minimal.container import Container

class ConfigOption (Datum) :
  u"""
  This class is designed for Config option handling.
  Based on Datum, this class adds two fields.
  
  **level**, unicode. Defines the option level. Three levels are available :
    - user : means this option can be changed by simple users
    - devel : <=> advanced. Means this option can be changed by developers
    - expert : only for expert
  """
  ordered_keys = tuple(list(Datum.ordered_keys) + [u'level'])
  csv_order = u';'.join(ordered_keys)

  def __init__ (self, name=None, nature=None, value=None, default_value=None,
               value_list=None, tooltip=None, group=None, alias=None,
               level=u'user', *t, **d):
    Datum.__init__(self, name=name, nature=nature, value=value,
                   default_value=default_value, value_list=value_list,
                   tooltip=tooltip, group=group, alias=alias, *t, **d)
    self._level = level

  def _setlevel(self, level) :
    level = self.convert(level, unicode)
    if level and isinstance(level, unicode):
      check_value(u'level', level, [u'user', u'devel', u'expert'])
    else :
      level = u'user'
    self._level = level

  level = property(
    fget=lambda self: self._level,
    fset=_setlevel)

class ConfigOptionContainer(Container):
  u"""
  A Container for ConfigOption objetcs
  
  >>> options = ConfigOptionContainer()
  >>> options[u'opt1'] = 1
  >>> print options[u'opt1'].level
  user
  """

  datum_type = ConfigOption

  def __init__ (self, *args, **kargs):

    # TODO: Python3: remove lines from here ...
    new_dict = {}
    for key, value in kargs.items() :
      new_dict[key.encode(ENCODING)] = value
    # ... to here
    new_dict['datum_type'] = ConfigOption

    super(ConfigOptionContainer, self).__init__(*args, **new_dict)

class Configuration(Container):

  def __init__(self, *args, **kargs):
    # TODO: Python3: remove lines from here ...
    new_dict = {}
    for key, value in kargs.items() :
      new_dict[key.encode(ENCODING)] = value
    # ... to here
    new_dict['book_type'] = ConfigOptionContainer

    super(Configuration, self).__init__(*args, **new_dict)
    self._group = None

class Settings(Container):

  def __init__(self, filename=None):
    # TODO: Python3: remove lines from here ...
    new_dict = {}
    new_dict['book_type'] = ConfigOptionContainer
    new_dict['name'] = u'Settings'
    super(Settings, self).__init__(**new_dict)
    self._group = None
    self._filename = filename
    if self._filename :
      self.importfile(self._filename)

  def sync(self):
    u"""
    Synchronizes settings with file (write Settings on disk)
    """
    if self._filename :
      self.exportfile(self._filename)

  def beginGroup(self, prefix):
    u"""
    Opens a group called 'prefix'
    
    :param prefix: group name
    :type prefix: :obj:`unicode`
    """
    self._group = prefix

  def endGroup(self):
    u"""
    Closes current group.
    
    :param prefix: group name
    :type prefix: :obj:`unicode`
    """
    self._group = None

  def getValue(self, key, default_datum):
    u"""
    :param key: value key
    :type key: :obj:`unicode`
    :param default_datum: datum to use if 'key' not found in settings
    :type default_datum: :class:`~pylot.core.ConfigOption`
    """
    if self._group and u'/' not in key :
      group = self._group
      key = key
    elif not self._group and len(key.split(u'/')) == 2 :
      group, key = key.split(u'/')
    else :
      # error
      raise IOError
    if not isinstance(default_datum, (ConfigOption, Datum)):
      default_datum = ConfigOption(key, value=default_datum)
    try :
      datum = self[group][key]
    except KeyError :
      return default_datum
    else :
      return datum

  def setValue(self, key, config_option):
    u"""
    :param key: value key
    :type key: :obj:`unicode`
    :param config_option: option you want to save on settings
    :type config_option: :class:`~pylot.core.ConfigOption`
    """
    if self._group and u'/' not in key :
      group = self._group
      key = key
    elif not self._group and len(key.split(u'/')) == 2 :
      group, key = key.split(u'/')
    else :
      raise IOError
    if not isinstance(config_option, (ConfigOption, Datum)):
      config_option = ConfigOption(key, value=config_option)
    if group not in self :
      self[group] = ConfigOptionContainer(alias=group)
    if key not in self[group] :
      self[group][key] = config_option
    else :
      self[group][key].update(config_option,
        mod_attrs=[u'value', u'default_value', u'value_list', u'level'])

  def setContainer(self, book):
    u"""
    :param book: book you want to save on settings
    :type book: :class:`~pylot.core.Container`
    """
    for datum_id, datum in book.items():
      self.setValue(datum_id, datum)

  def getContainer(self, default_book):
    u"""
    Returns book corresponding to current group.
    If no book found, returns default_book.

    :param default_book: book to use if no book found
    :type default_book: :class:`~pylot.core.Container`
    """
    try :
      return self[self._group]
    except KeyError :
      return default_book

def test():
  """
  >>> from _pylot.minimal.path import Path
  >>> config = Configuration()
  >>> path = Path(u'pyLotTests', u'doctests', u'data', u'config.csv')
  >>> config.upFromFile(path)
  >>> config[u'livre1'][u'book1opt4'].level
  u'expert'
  """

if __name__ == '__main__':
  import doctest
  doctest.testmod()
